/**
 ***********************************************************************************************************
 * 
 ***********************************************************************************************************
 * @file RBMaterial.h
 ***********************************************************************************************************/

#ifndef __RBMATERIAL_H__
#define __RBMATERIAL_H__

#ifdef  HEADER_ONCE
#pragma HEADER_ONCE
#endif

#ifdef  HEADER_MSG
#pragma HEADER_MSG(RBMaterial.h)
#endif

/***********************************************************************************************************
 * Includes
 ***********************************************************************************************************/
#include "RBRefObject.h"

#include "RBVector.h"
#include "RBTextureSampler.h"
#include "RBRenderProcess/RBEffectDataContainer.h"
#include "RBRenderProcess/RBShaderResourceView.h"

/***********************************************************************************************************
 * Forward declarations
 ***********************************************************************************************************/
class RBEffect;

/***********************************************************************************************************
 * Classes
 ***********************************************************************************************************/

class RBMaterial : public RBRefObject
{
    RBDeclareRTTI(RBMaterial, RBRefObject);
public:
				        RBMaterial      ();
    virtual            ~RBMaterial      ();

    enum CullMode
    {
        MCM_NONE = 0,
        MCM_FRONT,
        MCM_BACK,
    };

    enum AlphaFunction
    {
        AF_ZERO = 1,
        AF_ONE = 2,
        AF_SRC_COLOR = 3,
        AF_INV_SRC_COLOR = 4,
        AF_SRC_ALPHA = 5,
        AF_INV_SRC_ALPHA = 6,
        AF_DEST_ALPHA = 7,
        AF_INV_DEST_ALPHA = 8,
        AF_DEST_COLOR = 9,
        AF_INV_DEST_COLOR = 10,
        AF_SRC_ALPHA_SAT = 11,
        AF_BLEND_FACTOR = 14,
        AF_INV_BLEND_FACTOR = 15,
        AF_SRC1_COLOR = 16,
        AF_INV_SRC1_COLOR = 17,
        AF_SRC1_ALPHA = 18,
        AF_INV_SRC1_ALPHA = 19,
    };

    enum EBlendOp
    {
        BO_ADD	= 1,
        BO_SUBTRACT	= 2,
        BO_REV_SUBTRACT	= 3,
        BO_MIN	= 4,
        BO_MAX	= 5,
    };

    enum ECompFunc
    {
        CF_NEVER           = 1,
        CF_LESS            = 2,
        CF_EQUAL           = 3,
        CF_LESS_EQUAL      = 4,
        CF_GREATER         = 5,
        CF_NOT_EQUAL       = 6,
        CF_GREATER_EQUAL   = 7,
        CF_ALWAYS          = 8 
    };

    enum EStencilOp 
    {
        SO_KEEP       = 1,
        SO_ZERO       = 2,
        SO_REPLACE    = 3,
        SO_INCR_SAT   = 4,
        SO_DECR_SAT   = 5,
        SO_INVERT     = 6,
        SO_INCR       = 7,
        SO_DECR       = 8 
    };

#pragma warning(push)
#pragma warning(disable: 4201) // Nameless struct / use -fms-extensions with GCC
    union RBBlendState
    {
        uint32 Mask;
        struct  
        {
            bool            IsAlphaBlended  : 1;
            uint8           SrcBlend        : 5; // AlphaFunction
            uint8           DestBlend       : 5; // AlphaFunction
            uint8           BlendOp         : 3; // Type Is BlendOp

            uint8           SrcBlendAlpha   : 5; // AlphaFunction
            uint8           DestBlendAlpha  : 5; // AlphaFunction
            uint8           BlendOpAlpha    : 3; // Type Is BlendOp
        };
    };


    union RBRasterState
    {
        uint32 Mask;
        struct  
        {
            bool            IsWireframe : 1;
            bool            FrontCounterClockwise : 1;
            uint8           Cull : 3;               // CullMode

        };
    };

    union RBDepthStencilState
    {
        uint64 Mask;
        struct //63 bits
        {
            bool            ZEnable                     : 1;
            bool            ZWriteEnable                : 1;
            uint8           ZFunc                       : 4; // ECompFunc
            
            bool            StencilEnable               : 1;
            uint8           StencilMask                 : 8;
            uint8           StencilWriteMask            : 8;
            uint8           StencilRef                  : 8;

            uint8           StencilFrontFaceFunc        : 4; // ECompFunc
            uint8           StencilFrontFacePassOp      : 4; // EStencilOp
            uint8           StencilFrontFaceFailOp      : 4; // EStencilOp
            uint8           StencilFrontFaceDepthFailOp : 4; // EStencilOp

            uint8           StencilBackFaceFunc         : 4; // ECompFunc
            uint8           StencilBackFacePassOp       : 4; // EStencilOp
            uint8           StencilBackFaceFailOp       : 4; // EStencilOp
            uint8           StencilBackFaceDepthFailOp  : 4; // EStencilOp
        };
    };
#pragma warning(pop)

    RBColor                 Ambient;
    RBColor                 Diffuse;
    RBColor                 Specular;
    RBColor                 Emissive;
    float32                 Shininess;
    float32                 Opacity;
    float32                 Multiplier;

    bool                    IsAlphaTested;
    uint8                   AlphaRef;
    
    RBBlendState            BlendState;
    RBRasterState           RasterState;
    RBDepthStencilState     DepthState;

    const RBTextureSampler* GetSampler      (const RBStringID& eType) const;
    RBTextureSampler&       GetSampler      (const RBStringID& eType);

    virtual void            Serialize       (RBStream& kStream);

    RBEffect*               GetEffect       () const { return m_pEffect; }
    void                    SetEffect       (RBEffect* pEffect) { m_pEffect = pEffect; }

    uint32                  GetTechnique    () const { return m_uiTechnique; }
    void                    SetTechnique    (uint32 uiTechnique) { m_uiTechnique = uiTechnique; }

    RBEffectDataContainer   DataContainer;

    void                    CopyMembers     (RBMaterial* pDest);

private:
    RBEffect*               m_pEffect;
    uint32                  m_uiTechnique;
};

RBDeclareSmartPointer(RBMaterial);

/**************************************************************************************************************************************************************
 * 
 **************************************************************************************************************************************************************/
inline const RBTextureSampler* RBMaterial::GetSampler(const RBStringID& eType) const
{
    return DataContainer.RetrieveSampler(eType);
}

/***********************************************************************************************************
 *
 ***********************************************************************************************************/
inline RBTextureSampler& RBMaterial::GetSampler(const RBStringID& eType)
{
    return DataContainer.GetSampler(eType);
}

/***********************************************************************************************************/

#endif // __RBMATERIAL_H__